package com.randi.framework.domain.service.impl;

import com.randi.common.config.AppConfig;
import com.randi.common.constant.CacheConstants;
import com.randi.common.constant.Constants;
import com.randi.common.utils.ServletUtils;
import com.randi.common.utils.StringUtils;
import com.randi.common.utils.ip.AddressUtils;
import com.randi.common.utils.ip.IpUtils;
import com.randi.common.utils.uuid.IdUtils;
import com.randi.common.core.domain.model.LoginUser;
import com.randi.common.utils.token.JwtTokenUtils;
import com.randi.common.utils.RedisUtil;
import com.randi.framework.domain.service.ISysTokenService;
import eu.bitwalker.useragentutils.UserAgent;
import io.jsonwebtoken.Claims;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * token验证处理
 *
 * @author ziye
 */
@Component
@Service
public class SysTokenServiceImpl implements ISysTokenService {

    private String accessHeader;

    private String accessField;

    private Integer accessTokenExpire;

    private String refreshHeader;

    private String refreshField;

    private Integer refreshTokenExpire;

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private void init(AppConfig.TokenConfig tokenConfig) {
        accessHeader = tokenConfig.getAccess().getHeader();
        accessField = tokenConfig.getAccess().getFieldName();
        accessTokenExpire = tokenConfig.getAccess().getExpireTime();
        refreshHeader = tokenConfig.getRefresh().getHeader();
        refreshField = tokenConfig.getRefresh().getFieldName();
        refreshTokenExpire = tokenConfig.getRefresh().getExpireTime();
    }

    /**
     * 获取请求token
     *
     * @param request request
     * @return token
     */
    @Override
    public String getToken(HttpServletRequest request) {
        String token = request.getHeader(accessHeader);
        if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) {
            token = token.replace(Constants.TOKEN_PREFIX, "");
        }
        return token;
    }

    /**
     * 获取请求token
     *
     * @param request request
     * @return token
     */
    @Override
    public String getRefreshToken(HttpServletRequest request) {
        String token = request.getHeader(refreshHeader);
        if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) {
            token = token.replace(Constants.TOKEN_PREFIX, "");
        }
        return token;
    }

    /**
     * 验证令牌是否有效
     *
     * @param token token
     * @return 令牌
     */
    @Override
    public boolean verifyToken(String token) {
        long tokenExpired = JwtTokenUtils.getTokenExpired(token);
        if (tokenExpired <= 0) {
            return false;
        }

        Map<String, Object> map = JwtTokenUtils.checkToken(token);
        return map != null && map.containsKey(Constants.LOGIN_USER_KEY);
    }

    /**
     * 获取用户身份信息
     *
     * @return 用户信息
     */
    @Override
    public LoginUser getLoginUser(HttpServletRequest request) {
        // 获取请求携带的令牌
        String token = getToken(request);
        if (StringUtils.isEmpty(token)) {
            return null;
        }
        return this.getLoginUser(token);
    }

    /**
     * 获取验证用户身份信息
     *
     * @param uuid uuid
     * @return 用户信息
     */
    @Override
    public LoginUser getLoginUser(String uuid) {
        if (StringUtils.isEmpty(uuid)) {
            return null;
        }

        Claims claims = JwtTokenUtils.checkToken(uuid);
        if (StringUtils.isNull(claims)) {
            return null;
        }
        // 解析对应的权限以及用户信息
        String tokenId = (String) claims.get(Constants.LOGIN_USER_KEY);
        return redisUtil.getCache(getAccessTokenKey(tokenId));
    }

    /**
     * 获取刷新用户身份信息
     *
     * @param uuid uuid
     * @return 用户信息
     */
    @Override
    public LoginUser getRefreshLoginUser(String uuid) {
        if (StringUtils.isEmpty(uuid)) {
            return null;
        }
        Claims claims = JwtTokenUtils.checkToken(uuid);
        if (StringUtils.isNull(claims)) {
            return null;
        }
        // 解析对应的权限以及用户信息
        String tokenId = (String) claims.get(Constants.LOGIN_USER_KEY);
        return redisUtil.getCache(getRefreshTokenKey(tokenId));
    }

    /**
     * 设置用户代理信息
     *
     * @param loginUser 登录信息
     */
    @Override
    public void setUserAgent(LoginUser loginUser) {
        UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
        String ip = IpUtils.getIpAddress();
        loginUser.setIpAddress(ip);
        loginUser.setLoginTime(System.currentTimeMillis());
        loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
        loginUser.setBrowser(userAgent.getBrowser().getName());
        loginUser.setOs(userAgent.getOperatingSystem().getName());
    }

    /**
     * 获取 AccessTokenKey
     *
     * @param uuid uuid
     * @return AccessTokenKey
     */
    private String getAccessTokenKey(String uuid) {
        return CacheConstants.LOGIN_TOKEN_ACCESS_KEY + uuid;
    }

    /**
     * 获取 RefreshTokenKey
     *
     * @param uuid uuid
     * @return RefreshTokenKey
     */
    private String getRefreshTokenKey(String uuid) {
        return CacheConstants.LOGIN_TOKEN_REFRESH_KEY + uuid;
    }

    /**
     * 创建Token
     *
     * @param loginUser loginUser
     * @return tokenMap
     */
    @Override
    public HashMap<String, String> buildToken(LoginUser loginUser) {
        String uuid = IdUtils.fastUUID();

        HashMap<String, Object> claims = new HashMap<>();
        claims.put(Constants.LOGIN_USER_KEY, uuid);
        String accessToken = JwtTokenUtils.createToken(claims, accessTokenExpire);
        String refreshToken = JwtTokenUtils.createToken(claims, refreshTokenExpire);

        loginUser.setUuid(uuid);
        loginUser.setExpireTime(JwtTokenUtils.getTokenExpired(accessToken));
        setUserAgent(loginUser);

        // 缓存信息
        redisUtil.setCache(getAccessTokenKey(uuid), loginUser, JwtTokenUtils.getTokenExpired(accessToken));
        redisUtil.setCache(getRefreshTokenKey(uuid), loginUser, JwtTokenUtils.getTokenExpired(refreshToken));

        HashMap<String, String> resToken = new HashMap<>();
        resToken.put(accessField, Constants.TOKEN_PREFIX + accessToken);
        resToken.put(refreshField, Constants.TOKEN_PREFIX + refreshToken);
        return resToken;
    }

    /**
     * 移除Token缓存
     *
     * @param uuid uuid
     */
    @Override
    public void removeTokenCache(String uuid) {
        if (StringUtils.isEmpty(uuid)) {
            return;
        }
        if (redisUtil.hasKey(getAccessTokenKey(uuid))) {
            redisUtil.delete(getAccessTokenKey(uuid));
        }
        if (redisUtil.hasKey(getRefreshTokenKey(uuid))) {
            redisUtil.delete(getRefreshTokenKey(uuid));
        }
    }

    /**
     * 刷新 Token
     *
     * @param request request
     * @return tokenHashMap
     */
    @Override
    public HashMap<String, String> refreshToken(HttpServletRequest request) {
        String refreshToken = this.getRefreshToken(request);
        // 获取用户信息
        LoginUser loginUser = this.getRefreshLoginUser(refreshToken);
        this.removeTokenCache(loginUser.getUuid());
        return this.buildToken(loginUser);
    }

    /**
     * 更新登录用户信息
     *
     * @param loginUser loginUser
     */
    @Override
    public void updateUserInfo(LoginUser loginUser) {
        String uuid = loginUser.getUuid();
        Long expire = redisUtil.getExpire(getAccessTokenKey(uuid));
        redisUtil.setCache(getAccessTokenKey(uuid), loginUser, expire);
    }

}
